package com.example.chinesechess.domain.model

/**
 * Represents a Chinese chess board
 * The board is a 9x10 grid (9 columns, 10 rows)
 */
class ChessBoard {
    // 2D grid of pieces, null if no piece at that position
    private val board: Array<Array<ChessPiece?>> = Array(9) { Array(10) { null } }
    
    // Current side to move
    var currentSide: Side = Side.RED
        private set
    
    // History of moves
    private val moveHistory = mutableListOf<Move>()
    
    init {
        setupInitialPosition()
    }
    
    /**
     * Set up the initial position of all pieces
     */
    private fun setupInitialPosition() {
        // Red pieces (bottom side)
        // Chariot (车)
        setPiece(ChessPiece(PieceType.CHARIOT, Side.RED, Position(0, 9)))
        setPiece(ChessPiece(PieceType.CHARIOT, Side.RED, Position(8, 9)))
        
        // Horse (马)
        setPiece(ChessPiece(PieceType.HORSE, Side.RED, Position(1, 9)))
        setPiece(ChessPiece(PieceType.HORSE, Side.RED, Position(7, 9)))
        
        // Elephant (象)
        setPiece(ChessPiece(PieceType.ELEPHANT, Side.RED, Position(2, 9)))
        setPiece(ChessPiece(PieceType.ELEPHANT, Side.RED, Position(6, 9)))
        
        // Advisor (士)
        setPiece(ChessPiece(PieceType.ADVISOR, Side.RED, Position(3, 9)))
        setPiece(ChessPiece(PieceType.ADVISOR, Side.RED, Position(5, 9)))
        
        // General (帅)
        setPiece(ChessPiece(PieceType.GENERAL, Side.RED, Position(4, 9)))
        
        // Cannon (炮)
        setPiece(ChessPiece(PieceType.CANNON, Side.RED, Position(1, 7)))
        setPiece(ChessPiece(PieceType.CANNON, Side.RED, Position(7, 7)))
        
        // Soldier (兵)
        setPiece(ChessPiece(PieceType.SOLDIER, Side.RED, Position(0, 6)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.RED, Position(2, 6)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.RED, Position(4, 6)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.RED, Position(6, 6)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.RED, Position(8, 6)))
        
        // Black pieces (top side)
        // Chariot (车)
        setPiece(ChessPiece(PieceType.CHARIOT, Side.BLACK, Position(0, 0)))
        setPiece(ChessPiece(PieceType.CHARIOT, Side.BLACK, Position(8, 0)))
        
        // Horse (马)
        setPiece(ChessPiece(PieceType.HORSE, Side.BLACK, Position(1, 0)))
        setPiece(ChessPiece(PieceType.HORSE, Side.BLACK, Position(7, 0)))
        
        // Elephant (相)
        setPiece(ChessPiece(PieceType.ELEPHANT, Side.BLACK, Position(2, 0)))
        setPiece(ChessPiece(PieceType.ELEPHANT, Side.BLACK, Position(6, 0)))
        
        // Advisor (士)
        setPiece(ChessPiece(PieceType.ADVISOR, Side.BLACK, Position(3, 0)))
        setPiece(ChessPiece(PieceType.ADVISOR, Side.BLACK, Position(5, 0)))
        
        // General (将)
        setPiece(ChessPiece(PieceType.GENERAL, Side.BLACK, Position(4, 0)))
        
        // Cannon (炮)
        setPiece(ChessPiece(PieceType.CANNON, Side.BLACK, Position(1, 2)))
        setPiece(ChessPiece(PieceType.CANNON, Side.BLACK, Position(7, 2)))
        
        // Soldier (卒)
        setPiece(ChessPiece(PieceType.SOLDIER, Side.BLACK, Position(0, 3)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.BLACK, Position(2, 3)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.BLACK, Position(4, 3)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.BLACK, Position(6, 3)))
        setPiece(ChessPiece(PieceType.SOLDIER, Side.BLACK, Position(8, 3)))
    }
    
    /**
     * Get the piece at the given position
     */
    fun getPiece(position: Position): ChessPiece? {
        if (!position.isValid()) return null
        return board[position.x][position.y]
    }
    
    /**
     * Set a piece at the given position
     */
    private fun setPiece(piece: ChessPiece) {
        if (!piece.position.isValid()) return
        board[piece.position.x][piece.position.y] = piece
    }
    
    /**
     * Remove a piece from the given position
     */
    private fun removePiece(position: Position) {
        if (!position.isValid()) return
        board[position.x][position.y] = null
    }
    
    /**
     * Make a move on the board
     * @return true if the move was successful
     */
    fun makeMove(move: Move): Boolean {
        val fromPiece = getPiece(move.from) ?: return false
        
        // Check if it's the correct side's turn
        if (fromPiece.side != currentSide) {
            return false
        }
        
        // Check if the move is valid
        if (!isValidMove(move)) {
            return false
        }
        
        // Capture piece if there is one at the destination
        val capturedPiece = getPiece(move.to)
        
        // Update the piece's position
        val newPiece = fromPiece.copy(position = move.to)
        
        // Remove old piece and set new piece
        removePiece(move.from)
        setPiece(newPiece)
        
        // Add to move history
        moveHistory.add(move.copy(capturedPiece = capturedPiece))
        
        // Switch sides
        currentSide = currentSide.opposite()
        
        return true
    }
    
    /**
     * Check if a move is valid according to piece movement rules
     */
    fun isValidMove(move: Move): Boolean {
        val piece = getPiece(move.from) ?: return false
        val targetPiece = getPiece(move.to)
        
        // Can't capture your own pieces
        if (targetPiece != null && targetPiece.side == piece.side) {
            return false
        }
        
        // Implement movement rules for each piece type
        return when (piece.type) {
            PieceType.GENERAL -> isValidGeneralMove(piece, move.to)
            PieceType.ADVISOR -> isValidAdvisorMove(piece, move.to)
            PieceType.ELEPHANT -> isValidElephantMove(piece, move.to)
            PieceType.HORSE -> isValidHorseMove(piece, move.to)
            PieceType.CHARIOT -> isValidChariotMove(piece, move.to)
            PieceType.CANNON -> isValidCannonMove(piece, move.to)
            PieceType.SOLDIER -> isValidSoldierMove(piece, move.to)
        }
    }
    
    /**
     * Check if the move puts the player's general in check
     */
    fun wouldBeInCheck(move: Move, side: Side): Boolean {
        // Make a copy of the current board
        val tempBoard = ChessBoard()
        tempBoard.copyPositionFrom(this)
        
        // Try the move
        val piece = tempBoard.getPiece(move.from) ?: return false
        val newPiece = piece.copy(position = move.to)
        tempBoard.removePiece(move.from)
        tempBoard.setPiece(newPiece)
        
        // Check if the general is in check after the move
        return tempBoard.isInCheck(side)
    }
    
    /**
     * Copy the position from another board
     */
    fun copyPositionFrom(other: ChessBoard) {
        for (x in 0..8) {
            for (y in 0..9) {
                board[x][y] = other.board[x][y]?.copy()
            }
        }
        currentSide = other.currentSide
    }
    
    /**
     * Check if the given side's general is in check
     */
    fun isInCheck(side: Side): Boolean {
        // Find the general
        val general = findGeneral(side) ?: return false
        
        // Check if any opponent piece can capture the general
        for (x in 0..8) {
            for (y in 0..9) {
                val piece = board[x][y] ?: continue
                if (piece.side != side) {
                    val move = Move(piece.position, general.position)
                    if (isValidMove(move)) {
                        return true
                    }
                }
            }
        }
        
        return false
    }
    
    /**
     * Find the general of the given side
     */
    private fun findGeneral(side: Side): ChessPiece? {
        for (x in 0..8) {
            for (y in 0..9) {
                val piece = board[x][y]
                if (piece?.type == PieceType.GENERAL && piece.side == side) {
                    return piece
                }
            }
        }
        return null
    }
    
    /**
     * Check if the current player is in checkmate
     */
    fun isCheckmate(): Boolean {
        if (!isInCheck(currentSide)) {
            return false
        }
        
        // Check if any move can get out of check
        for (x in 0..8) {
            for (y in 0..9) {
                val piece = board[x][y] ?: continue
                if (piece.side == currentSide) {
                    for (toX in 0..8) {
                        for (toY in 0..9) {
                            val move = Move(piece.position, Position(toX, toY))
                            if (isValidMove(move) && !wouldBeInCheck(move, currentSide)) {
                                return false
                            }
                        }
                    }
                }
            }
        }
        
        return true
    }
    
    /**
     * Get all valid moves for a piece at the given position
     */
    fun getValidMoves(position: Position): List<Position> {
        val piece = getPiece(position) ?: return emptyList()
        val validMoves = mutableListOf<Position>()
        
        for (x in 0..8) {
            for (y in 0..9) {
                val targetPosition = Position(x, y)
                val move = Move(position, targetPosition)
                if (isValidMove(move) && !wouldBeInCheck(move, piece.side)) {
                    validMoves.add(targetPosition)
                }
            }
        }
        
        return validMoves
    }
    
    /**
     * Implementation of movement rules for each piece type
     */
    
    private fun isValidGeneralMove(piece: ChessPiece, to: Position): Boolean {
        // General can only move within the palace
        if (!to.isInPalace(piece.side)) {
            return false
        }
        
        val dx = Math.abs(to.x - piece.position.x)
        val dy = Math.abs(to.y - piece.position.y)
        
        // General can only move one step orthogonally
        return (dx == 1 && dy == 0) || (dx == 0 && dy == 1)
    }
    
    private fun isValidAdvisorMove(piece: ChessPiece, to: Position): Boolean {
        // Advisor can only move within the palace
        if (!to.isInPalace(piece.side)) {
            return false
        }
        
        val dx = Math.abs(to.x - piece.position.x)
        val dy = Math.abs(to.y - piece.position.y)
        
        // Advisor can only move one step diagonally
        return dx == 1 && dy == 1
    }
    
    private fun isValidElephantMove(piece: ChessPiece, to: Position): Boolean {
        // Elephant cannot cross the river
        if (to.isAcrossRiver(piece.side)) {
            return false
        }
        
        val dx = Math.abs(to.x - piece.position.x)
        val dy = Math.abs(to.y - piece.position.y)
        
        // Elephant moves exactly two points diagonally
        if (dx != 2 || dy != 2) {
            return false
        }
        
        // Check for blocking piece at the elephant's eye
        val eyeX = (piece.position.x + to.x) / 2
        val eyeY = (piece.position.y + to.y) / 2
        
        return getPiece(Position(eyeX, eyeY)) == null
    }
    
    private fun isValidHorseMove(piece: ChessPiece, to: Position): Boolean {
        val dx = Math.abs(to.x - piece.position.x)
        val dy = Math.abs(to.y - piece.position.y)
        
        // Horse moves one step orthogonally and then one step diagonally
        if (!((dx == 1 && dy == 2) || (dx == 2 && dy == 1))) {
            return false
        }
        
        // Check for blocking piece (horse leg)
        val legX = if (dx == 1) piece.position.x else piece.position.x + (to.x - piece.position.x) / 2
        val legY = if (dy == 1) piece.position.y else piece.position.y + (to.y - piece.position.y) / 2
        
        return getPiece(Position(legX, legY)) == null
    }
    
    private fun isValidChariotMove(piece: ChessPiece, to: Position): Boolean {
        val dx = to.x - piece.position.x
        val dy = to.y - piece.position.y
        
        // Chariot moves any distance orthogonally
        if (dx != 0 && dy != 0) {
            return false
        }
        
        // Check for blocking pieces along the path
        if (dx != 0) {
            val step = if (dx > 0) 1 else -1
            var x = piece.position.x + step
            while (x != to.x) {
                if (getPiece(Position(x, piece.position.y)) != null) {
                    return false
                }
                x += step
            }
        } else if (dy != 0) {
            val step = if (dy > 0) 1 else -1
            var y = piece.position.y + step
            while (y != to.y) {
                if (getPiece(Position(piece.position.x, y)) != null) {
                    return false
                }
                y += step
            }
        }
        
        return true
    }
    
    private fun isValidCannonMove(piece: ChessPiece, to: Position): Boolean {
        val dx = to.x - piece.position.x
        val dy = to.y - piece.position.y
        
        // Cannon moves any distance orthogonally
        if (dx != 0 && dy != 0) {
            return false
        }
        
        val targetPiece = getPiece(to)
        var pieceCount = 0
        
        // Count pieces along the path
        if (dx != 0) {
            val step = if (dx > 0) 1 else -1
            var x = piece.position.x + step
            while (x != to.x) {
                if (getPiece(Position(x, piece.position.y)) != null) {
                    pieceCount++
                }
                x += step
            }
        } else if (dy != 0) {
            val step = if (dy > 0) 1 else -1
            var y = piece.position.y + step
            while (y != to.y) {
                if (getPiece(Position(piece.position.x, y)) != null) {
                    pieceCount++
                }
                y += step
            }
        }
        
        // Cannon needs exactly one screen piece to capture
        // Or no screen pieces to move
        return (targetPiece != null && pieceCount == 1) || (targetPiece == null && pieceCount == 0)
    }
    
    private fun isValidSoldierMove(piece: ChessPiece, to: Position): Boolean {
        val dx = to.x - piece.position.x
        val dy = to.y - piece.position.y
        
        // Soldier always moves forward (relative to its side) or horizontally after crossing river
        val forwardDir = if (piece.side == Side.RED) -1 else 1
        
        if (piece.position.isAcrossRiver(piece.side)) {
            // After crossing the river, can move forward or horizontally
            return (dy == 0 && Math.abs(dx) == 1) || (dx == 0 && dy == forwardDir)
        } else {
            // Before crossing the river, can only move forward
            return dx == 0 && dy == forwardDir
        }
    }
    
    /**
     * Get the most recent move
     */
    fun getLastMove(): Move? {
        return moveHistory.lastOrNull()
    }
    
    /**
     * Undo the last move
     */
    fun undoLastMove(): Boolean {
        val lastMove = moveHistory.lastOrNull() ?: return false
        
        // Get the piece that moved
        val piece = getPiece(lastMove.to) ?: return false
        
        // Move the piece back
        val originalPiece = piece.copy(position = lastMove.from)
        removePiece(lastMove.to)
        setPiece(originalPiece)
        
        // Restore captured piece if any
        lastMove.capturedPiece?.let {
            setPiece(it)
        }
        
        // Remove move from history
        moveHistory.removeAt(moveHistory.size - 1)
        
        // Switch back sides
        currentSide = currentSide.opposite()
        
        return true
    }
    
    /**
     * Get all pieces on the board
     */
    fun getAllPieces(): List<ChessPiece> {
        val pieces = mutableListOf<ChessPiece>()
        
        for (x in 0..8) {
            for (y in 0..9) {
                board[x][y]?.let { pieces.add(it) }
            }
        }
        
        return pieces
    }
}

/**
 * Represents a move on the chess board
 * @param from Starting position
 * @param to Ending position
 * @param capturedPiece Piece that was captured (if any)
 */
data class Move(
    val from: Position,
    val to: Position,
    val capturedPiece: ChessPiece? = null
) 